package com.xr.service.impl;

import com.baomidou.mybatisplus.entity.TableInfo;
import com.baomidou.mybatisplus.enums.SqlMethod;
import com.baomidou.mybatisplus.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.mapper.*;
import com.baomidou.mybatisplus.plugins.Page;
import com.baomidou.mybatisplus.toolkit.*;
import com.xr.service.IBaseService;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.session.SqlSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;

/**
 * @author Arnold Yand
 * @summary 基础service操作定义
 * @time 2018/4/13 09:43
 */
public class BaseServiceImpl<M extends BaseMapper<T>, T> implements IBaseService<T> {

  protected Logger logger = LoggerFactory.getLogger(this.getClass());

  @Autowired
  protected M baseMapper;

  protected Wrapper<T> buildWhereWrapper(Map param) throws Exception {
    EntityWrapper wrapper = new EntityWrapper();
    if(param != null){
      for(Object key : param.keySet()){
        wrapper.where(key + " = {0}", param.get(key));
      }
    }
    return wrapper;
  }

  /**
   * <p>
   * 判断数据库操作是否成功
   * </p>
   * <p>
   * 注意！！ 该方法为 Integer 判断，不可传入 int 基本类型
   * </p>
   *
   * @param result 数据库操作返回影响条数
   * @return boolean
   */
  protected static boolean retBool(Integer result) {
    return SqlHelper.retBool(result);
  }

  @SuppressWarnings("unchecked")
  protected Class<T> currentModelClass() {
    return ReflectionKit.getSuperClassGenricType(getClass(), 1);
  }

  /**
   * <p>
   * 批量操作 SqlSession
   * </p>
   */
  protected SqlSession sqlSessionBatch() {
    return SqlHelper.sqlSessionBatch(currentModelClass());
  }

  /**
   * 获取SqlStatement
   *
   * @param sqlMethod
   * @return
   */
  protected String sqlStatement(SqlMethod sqlMethod) {
    return SqlHelper.table(currentModelClass()).getSqlStatement(sqlMethod.getMethod());
  }

  @Transactional(rollbackFor = Exception.class)
  @Override
  public T insert(T entity) {
    if (1 != baseMapper.insert(entity)) {
      throw new IllegalStateException("insert failed.");
    }
    return entity;
  }

  @Transactional(rollbackFor = Exception.class)
  @Override
  public Integer insertBatch(List<T> entityList) {
    return insertBatch(entityList, 30);
  }

  /**
   * 批量插入
   *
   * @param entityList
   * @param batchSize
   * @return
   */
  @Transactional(rollbackFor = Exception.class)
  @Override
  public Integer insertBatch(List<T> entityList, int batchSize) {
    if (CollectionUtils.isEmpty(entityList)) {
      throw new IllegalArgumentException("Error: entityList must not be empty");
    }
    int size = entityList.size();
    try (SqlSession batchSqlSession = sqlSessionBatch()) {
      String sqlStatement = sqlStatement(SqlMethod.INSERT_ONE);
      for (int i = 0; i < size; i++) {
        batchSqlSession.insert(sqlStatement, entityList.get(i));
        if (i >= 1 && i % batchSize == 0) {
          batchSqlSession.flushStatements();
        }
      }
      batchSqlSession.flushStatements();
    } catch (Throwable e) {
      throw new MybatisPlusException("Error: Cannot execute insertBatch Method. Cause", e);
    }

    return size;
  }

  /**
   * <p>
   * TableId 注解存在更新记录，否插入一条记录
   * </p>
   *
   * @param entity 实体对象
   * @return boolean
   */
  @Transactional(rollbackFor = Exception.class)
  @Override
  public T insertOrUpdate(T entity) {
    if (null != entity) {
      Class<?> cls = entity.getClass();
      TableInfo tableInfo = TableInfoHelper.getTableInfo(cls);
      if (null != tableInfo && StringUtils.isNotEmpty(tableInfo.getKeyProperty())) {
        Object idVal = ReflectionKit.getMethodValue(cls, entity, tableInfo.getKeyProperty());
        if (StringUtils.checkValNull(idVal)) {
          return insert(entity);
        } else {
          /*
           * 更新成功直接返回，失败执行插入逻辑
           */
          if (1 == updateById(entity)) {
            // do nothing
          } else {
            insert(entity);
          }

          return entity;
        }
      } else {
        throw new MybatisPlusException("Error:  Can not execute. Could not find @TableId.");
      }
    }

    return entity;
  }

  @Transactional(rollbackFor = Exception.class)
  @Override
  public Integer insertOrUpdateBatch(List<T> entityList) {
    return insertOrUpdateBatch(entityList, 30);
  }

  @Transactional(rollbackFor = Exception.class)
  @Override
  public Integer insertOrUpdateBatch(List<T> entityList, int batchSize) {
    return insertOrUpdateBatch(entityList, batchSize, true);
  }

  /**
   * 批量插入修改
   *
   * @param entityList 实体对象列表
   * @param batchSize  批量刷新个数
   * @param selective  是否滤掉空字段
   * @return boolean
   */
  private Integer insertOrUpdateBatch(List<T> entityList, int batchSize, boolean selective) {
    if (CollectionUtils.isEmpty(entityList)) {
      throw new IllegalArgumentException("Error: entityList must not be empty");
    }
    int size = entityList.size();
    try (SqlSession batchSqlSession = sqlSessionBatch()) {
      for (int i = 0; i < size; i++) {
        insertOrUpdate(entityList.get(i));
        if (i >= 1 && i % batchSize == 0) {
          batchSqlSession.flushStatements();
        }
      }
      batchSqlSession.flushStatements();
    } catch (Throwable e) {
      throw new MybatisPlusException("Error: Cannot execute insertOrUpdateBatch Method. Cause", e);
    }
    return size;
  }

  @Transactional(rollbackFor = Exception.class)
  @Override
  public Integer deleteById(Serializable id) {
    return baseMapper.deleteById(id);
  }

  @Transactional(rollbackFor = Exception.class)
  @Override
  public Integer deleteByMap(Map<String, Object> columnMap) {
    if (MapUtils.isEmpty(columnMap)) {
      throw new MybatisPlusException("deleteByMap columnMap is empty.");
    }
    return baseMapper.deleteByMap(columnMap);
  }

  @Transactional(rollbackFor = Exception.class)
  @Override
  public Integer delete(Wrapper<T> wrapper) {
    return baseMapper.delete(wrapper);
  }

  @Transactional(rollbackFor = Exception.class)
  @Override
  public Integer deleteBatchIds(Collection<? extends Serializable> idList) {
    return baseMapper.deleteBatchIds(idList);
  }

  @Transactional(rollbackFor = Exception.class)
  @Override
  public Integer updateById(T entity) {
    return baseMapper.updateById(entity);
  }

  @Transactional(rollbackFor = Exception.class)
  @Override
  public T update(T entity, Wrapper<T> wrapper) {
    baseMapper.update(entity, wrapper);
    return entity;
  }

  @Transactional(rollbackFor = Exception.class)
  @Override
  public Integer updateBatchById(List<T> entityList) {
    return updateBatchById(entityList, 30);
  }

  @Transactional(rollbackFor = Exception.class)
  @Override
  public Integer updateBatchById(List<T> entityList, int batchSize) {
    return updateBatchById(entityList, batchSize, true);
  }

  /**
   * 根据主键ID进行批量修改
   *
   * @param entityList 实体对象列表
   * @param batchSize  批量刷新个数
   * @param selective  是否滤掉空字段
   * @return boolean
   */
  private Integer updateBatchById(List<T> entityList, int batchSize, boolean selective) {
    if (CollectionUtils.isEmpty(entityList)) {
      throw new IllegalArgumentException("Error: entityList must not be empty");
    }
    int size = entityList.size();
    try (SqlSession batchSqlSession = sqlSessionBatch()) {
      SqlMethod sqlMethod = selective ? SqlMethod.UPDATE_BY_ID : SqlMethod.UPDATE_ALL_COLUMN_BY_ID;
      String sqlStatement = sqlStatement(sqlMethod);
      for (int i = 0; i < size; i++) {
        MapperMethod.ParamMap<T> param = new MapperMethod.ParamMap<>();
        param.put("et", entityList.get(i));
        batchSqlSession.update(sqlStatement, param);
        if (i >= 1 && i % batchSize == 0) {
          batchSqlSession.flushStatements();
        }
      }
      batchSqlSession.flushStatements();
    } catch (Throwable e) {
      throw new MybatisPlusException("Error: Cannot execute updateBatchById Method. Cause", e);
    }
    return size;
  }

  @Override
  public T selectById(Serializable id) {
    return baseMapper.selectById(id);
  }

  @Override
  public List<T> selectBatchIds(Collection<? extends Serializable> idList) {
    return baseMapper.selectBatchIds(idList);
  }

  @Override
  public List<T> selectByMap(Map<String, Object> columnMap) {
    return baseMapper.selectByMap(columnMap);
  }

  @Override
  public T selectOne(Wrapper<T> wrapper) {
    return SqlHelper.getObject(baseMapper.selectList(wrapper));
  }

  @Override
  public Map<String, Object> selectMap(Wrapper<T> wrapper) {
    return SqlHelper.getObject(baseMapper.selectMaps(wrapper));
  }

  @Override
  public Object selectObj(Wrapper<T> wrapper) {
    return SqlHelper.getObject(baseMapper.selectObjs(wrapper));
  }

  @Override
  public int selectCount(Wrapper<T> wrapper) {
    return SqlHelper.retCount(baseMapper.selectCount(wrapper));
  }

  @Override
  public List<T> selectList(Wrapper<T> wrapper) {
    return baseMapper.selectList(wrapper);
  }

  @Override
  public Page<T> selectPage(Page<T> page) {
    return selectPage(page, Condition.EMPTY);
  }

  @Override
  public List<Map<String, Object>> selectMaps(Wrapper<T> wrapper) {
    return baseMapper.selectMaps(wrapper);
  }

  @Override
  public List<Object> selectObjs(Wrapper<T> wrapper) {
    return baseMapper.selectObjs(wrapper);
  }

  @Override
  public Page<Map<String, Object>> selectMapsPage(Page page, Wrapper<T> wrapper) {
    wrapper = (Wrapper<T>) SqlHelper.fillWrapper(page, wrapper);
    page.setRecords(baseMapper.selectMapsPage(page, wrapper));
    return page;
  }

  @Override
  public Page<T> selectPage(Page<T> page, Wrapper<T> wrapper) {
    wrapper = (Wrapper<T>) SqlHelper.fillWrapper(page, wrapper);
    page.setRecords(baseMapper.selectPage(page, wrapper));
    return page;
  }

}
